#include "packet_headers/etherhdr.h"
#include "packet_headers/iphdr.h"
#include "packet_headers/tcphdr.h"
#include "packet_headers/icmphdr.h"
#include "packet_headers/arphdr.h"
#include "packet_headers/udphdr.h"
#include "packet_headers/flags.h"
#include "network/info.h"
#include "network/raw_connection.h"
#include "network/raw_listener.h"
#include "network/hw_translate.h"
#include "network/packet.h"
#include "network/multifile.h"
#include "Errors/errors.h"
#include <errno.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>

char * usage = "raw <interface> <mode> [...]\n\
		\t<mode=interactive>: <protocol> [...] \n\
		\t\t-precedence [precedence] \n\
		\t\t-diff [d,t,r], -id [id] \n\
		\t\t<protocol=tcp> -srcip [source ip] -destip [dest ip] \n\
		\t\t\t-srcport [source port] -destport [destination port] \n\
		\t\t\t-flag [syn, fin, ack, urg, push, reset] \n\
		\t\t<protocol=udp> -srcip [source ip] -destip [dest ip] \n\
		\t\t\t-srcport [source port] -destport [destination port] \n\
		\t\t<protocol=icmp> -srcip [source ip] -destip [dest ip] \n\
		\t\t\t-type [echo, reply] -code [code] \n\
		\t\t<protocol=arp> -srcip [source ip] -destip [dest ip] \n\
		\t\t\t-desthw [destination hw address] -op [request, reply] \n\
		\t\t-corrupt [bits] -time [duration] \n\
		\t<mode=batch> <filename>\n";

int main(int argc, char ** argv) {
	if(argc < 3) {
		printf("Usage: %s\n", usage);
		return -1;
	}

	// Set the interface
	set_interface(argv[1]);

	unsigned int protocol = 0;
	unsigned char TOS=0, tcpflags=0;
	unsigned int srcaddr=0, destaddr=0;
	unsigned short srcport=0, destport=0;
	unsigned char icmptype=8, icmpcode=0;
	unsigned char * desthw;
	unsigned short op = 1;
	unsigned short ip_id = 0;
	unsigned short checkcorrupt = 0;
	int seconds = 5;

	int last_arg = 0;

	// Select mode
	if(strncmp(argv[2], "interactive", 11) == 0) {
		last_arg = 2;
		if(argc < 4) {
			printf("Usage: %s\n", usage);
			return -1;
		}
		if(strncmp(argv[3], "tcp", 3) == 0) {
			protocol = 1;
			last_arg = 3;
			if(argc < 6 || strncmp(argv[4],"-srcip",6) != 0) {
				printf("Usage: %s\n", usage);
				return -1;
			}
			else {
				last_arg = 5;
				srcaddr = inet_addr(argv[5]);
			}
			if(argc < 8 || strncmp(argv[6],"-destip",7) != 0) {
				printf("Usage: %s\n", usage);
				return -1;
			}
			else {
				last_arg = 7;
				destaddr = inet_addr(argv[7]);
			}
			if(argc < 10 || strncmp(argv[8], "-srcport",8) != 0) {
				printf("Usage: %s\n", usage);
				return -1;
			}
			else {
				last_arg = 9;
				srcport = atoi(argv[9]);
			}
			if(argc < 12 || strncmp(argv[10], "-destport",9) != 0) {
				printf("Usage: %s\n", usage);
				return -1;
			}
			else {
				last_arg = 11;
				destport = atoi(argv[11]);
			}
			if(argc > 12) {
				if(strncmp(argv[last_arg+1], "-flag", 5) == 0) {
					last_arg += 1;
					if(argc < last_arg+1) {
						printf("Usage: %s\n", usage);
						return -1;
					}
					else {
						last_arg += 1;
						if(strncmp(argv[last_arg], "syn",3) == 0) {
							tcpflags = TCP_Flags(false,false,false,false,true,false);
						}
						else if(strncmp(argv[last_arg], "fin",3) == 0) {
							tcpflags = TCP_Flags(false,false,false,false,false,true);
						}
						else if(strncmp(argv[last_arg], "ack",3) == 0) {
							tcpflags = TCP_Flags(true,false,false,false,false,false);
						}
						else if(strncmp(argv[last_arg], "urg",3) == 0) {
							tcpflags = TCP_Flags(false,true,false,false,false,false);
						}
						else if(strncmp(argv[last_arg], "push",4) == 0) {
							tcpflags = TCP_Flags(false,false,true,false,false,false);
						}
						else if(strncmp(argv[last_arg], "reset",3) == 0) {
							tcpflags = TCP_Flags(false,false,false,true,false,false);
						}
					}
				}
			}
		}
		else if(strncmp(argv[3], "udp", 3) == 0) {
			protocol = 2;
			last_arg = 3;
			if(argc < 6 || strncmp(argv[4],"-srcip",6) != 0) {
				printf("Usage: %s\n", usage);
				return -1;
			}
			else {
				last_arg = 5;
				srcaddr = inet_addr(argv[5]);
			}
			if(argc < 8 || strncmp(argv[6],"-destip",7) != 0) {
				printf("Usage: %s\n", usage);
				return -1;
			}
			else {
				last_arg = 7;
				destaddr = inet_addr(argv[7]);
			}
			if(argc < 10 || strncmp(argv[8], "-srcport",8) != 0) {
				printf("Usage: %s\n", usage);
				return -1;
			}
			else {
				last_arg = 9;
				srcport = atoi(argv[9]);
			}
			if(argc < 12 || strncmp(argv[10], "-destport",9) != 0) {
				printf("Usage: %s\n", usage);
				return -1;
			}
			else {
				last_arg = 11;
				destport = atoi(argv[11]);
			}
		}
		else if(strncmp(argv[3],"icmp",4) == 0) {
			protocol = 3;
			last_arg = 3;
			if(argc < 6 || strncmp(argv[4],"-srcip",6) != 0) {
				printf("Usage: %s\n", usage);
				return -1;
			}
			else {
				last_arg = 5;
				srcaddr = inet_addr(argv[5]);
			}
			if(argc < 8 || strncmp(argv[6],"-destip",7) != 0) {
				printf("Usage: %s\n", usage);
				return -1;
			}
			else {
				last_arg = 7;
				destaddr = inet_addr(argv[7]);
			}
			if(argc > 8) {
				last_arg = 8;
				if(strncmp(argv[8], "-type", 5) == 0) {
					if(argc < 10) {
						printf("Usage: %s\n", usage);
						return -1;
					}
					else {
						last_arg = 9;
						if(strncmp(argv[9],"request",7) == 0) {
							icmptype = 8;
						}
						else if(strncmp(argv[9], "reply",5) == 0) {
							icmptype = 0;
						}
					}
				}
				if(argc > last_arg+1 &&
						strncmp(argv[last_arg+1],"-code",4) == 0) {
					if(argc <= last_arg+1) {
						printf("Usage: %s\n", usage);
						return -1;
					}
					else {
						last_arg += 2;
						icmpcode = atoi(argv[last_arg]);
					}
				}
			}
		}
		if(strncmp(argv[3], "arp", 3) == 0) {
			protocol = 4;
			last_arg = 3;
			if(argc < 6 || strncmp(argv[4],"-srcip",6) != 0) {
				printf("Usage: %s\n", usage);
				return -1;
			}
			else {
				last_arg = 5;
				srcaddr = inet_addr(argv[5]);
			}
			if(argc < 8 || strncmp(argv[6],"-destip",7) != 0) {
				printf("Usage: %s\n", usage);
				return -1;
			}
			else {
				last_arg = 7;
				destaddr = inet_addr(argv[7]);
			}
			if(argc < 10 || strncmp(argv[8], "-desthw",7) != 0) {
				printf("Usage: %s\n", usage);
				return -1;
			}
			else {
				last_arg = 9;
				desthw = (unsigned char*) get_hw_addr(argv[9]);
			}
			if(argc > 11 && strncmp(argv[10], "-op", 3)) {
				last_arg = 11;
				if(strncmp(argv[11], "request", 7) == 0) {
					op = 1;
				}
				else
					op = 2;
			}
		}
		else {
			unsigned char precedence = 0;
			bool d=false,t=false,r=false;
			if(argc > last_arg + 1) {

				if(strncmp(argv[last_arg+1],"-precedence",11) == 0) {
					last_arg += 1;
					if(argc < last_arg+1) {
						printf("Usage: %s\n", usage);
						return -1;
					}
					else {
						last_arg += 1;
						precedence = atoi(argv[last_arg]);
					}
				}
			}
			if(argc > last_arg + 1) {
				if(strncmp(argv[last_arg+1],"-diff",5) == 0) {
					last_arg += 1;
					if(argc < last_arg+1) {
						printf("Usage: %s\n", usage);
						return -1;
					}
					else {
						last_arg += 1;
						if(argv[last_arg][0] == 'd') {
							d = true;
						}
						if(argv[last_arg][0] == 't') {
							t = true;
						}
						if(argv[last_arg][0] == 'r') {
							r = true;
						}
					}
				}
			}
			TOS = IP_TOS_FILL(precedence, d, t, r);
			if(argc > last_arg + 1) {
				if(last_arg+1 < argc) {
					if(strncmp(argv[last_arg+1],"-id",3) == 0) {
						last_arg += 1;
						if(argc < last_arg+1) {
							printf("Usage: %s\n", usage);
							return -1;
						}
						else {
							last_arg += 1;
							ip_id = atoi(argv[last_arg]);
						}
					}
				}
			}
		}
		if(argc > last_arg+1) {
			if(argc > last_arg+2) {
				if(strncmp(argv[last_arg+1], "-corrupt",8) == 0) {
					last_arg += 2;
					checkcorrupt = atoi(argv[last_arg]);
				}
			}
		}
		if(argc > last_arg+1) {
			if(argc > last_arg+2) {
					if(strncmp(argv[last_arg+1], "-time",8) == 0) {
						last_arg += 2;
						seconds = atoi(argv[last_arg]);
					}
			}
		}
		RawConnection rc(argv[1]);
		if(rc.state != 0) {
			printf("Error: %s", get_error_message());
			return -1;
		}
		RawListener rl(argv[1], seconds*1000+1);
		if(rl.state < 0) {
			printf("Error: %s", get_error_message());
			return -1;
		}
		char data[900];
		unsigned char * buf;

		unsigned int len = 0;

		if(protocol == 1) {
			buf = tcp_packet(argv[5], argv[7],
					TOS, ip_id, data, 900, srcport, destport, 11,
					14, tcpflags, checkcorrupt);
			len += 900 + sizeof(struct ipheader) + sizeof(struct tcpheader);
		}
		else if(protocol == 2) {
			buf = udp_packet(argv[5], argv[7],
					TOS, ip_id, data, 900, srcport, destport, checkcorrupt);
			len += 900 + sizeof(struct ipheader) + sizeof(struct udpheader);
		}
		else if(protocol == 3) {
			buf = icmp_packet(argv[5], argv[7],
					TOS, ip_id, icmptype, icmpcode, 11, 0, checkcorrupt);
			len += sizeof(struct ipheader) + sizeof(struct icmpheader);
		}
		else if(protocol == 4) {
			buf = arp_packet(op, argv[5], (char*)desthw, argv[7]);
			len += sizeof(struct arp_packet);
		}

		int bytes = rc.send((char*)buf, len);
		printf("%d bytes sent. Responses: \n",bytes);

		// Get incoming traffic
		char inbuf[1500];
		while(rl.recv(inbuf, 1500) > 0) {
			struct etherheader * etherhdr = (struct etherheader*)inbuf;
			if(ntohs(etherhdr->ether_type) == ARP_FRAME_TYPE) {
				struct arp_packet * arphdr = (struct arp_packet*) inbuf;
				printf("ARP ");
				if(ntohs(arphdr->op) == 1)
					printf("request ");
				else
					printf("reply ");
				printf("from %02x:%02x:%02x:%02x:%02x:%02x id: %02x:%02x:%02x:%02x:%02x:%02x\n",
						(arphdr->src_hw_addr)[0], (arphdr->src_hw_addr)[1],
						(arphdr->src_hw_addr)[2], (arphdr->src_hw_addr)[3],
						(arphdr->src_hw_addr)[4], (arphdr->src_hw_addr)[5],
						(arphdr->sndr_hw_addr)[0], (arphdr->sndr_hw_addr)[1],
						(arphdr->sndr_hw_addr)[2], (arphdr->sndr_hw_addr)[3],
						(arphdr->sndr_hw_addr)[4], (arphdr->sndr_hw_addr)[5]);
			}
			else if(ntohs(etherhdr->ether_type) == 0x0800) {
				struct ipheader* iphdr = (struct ipheader*)(inbuf+sizeof(struct etherheader));
				struct tcpheader* tcphdr = (struct tcpheader*)
										(inbuf+sizeof(struct etherheader)+sizeof(struct ipheader));
				struct udpheader* udphdr = (struct udpheader*)
										(inbuf+sizeof(struct etherheader)+sizeof(struct ipheader));
				struct icmpheader* icmphdr = (struct icmpheader*)
										(inbuf+sizeof(struct etherheader)+sizeof(struct ipheader));
				unsigned char * srcaddr = (unsigned char*)&(iphdr->src);
				if(iphdr->proto == IPPROTO_TCP) {
					printf("TCP packet from %d.%d.%d.%d on port %d\n",
							srcaddr[0],srcaddr[1],srcaddr[2],srcaddr[3],
							ntohs(tcphdr->dest_port));
				}
				else if(iphdr->proto == IPPROTO_UDP) {
					printf("UDP datagram from %d.%d.%d.%d on port %d\n",
												srcaddr[0],srcaddr[1],srcaddr[2],srcaddr[3],
												ntohs(udphdr->dest_port));
				}
				else if(iphdr->proto == 1) {
					printf("ICMP ");
					if(icmphdr->type == 8) {
						printf("request ");
					}
					else if(icmphdr->type == 0) {
						printf("reply ");
					}
					else {
						printf("message ");
					}
					printf("from %d.%d.%d.%d with code %d",
							srcaddr[0],srcaddr[1],srcaddr[2],srcaddr[3],
							icmphdr->code);
				}
			}
		}

	}
	else if(strncmp(argv[2], "batch", 5) == 0) {
		if(argc < 4) {
			printf("Usage: %s\n", usage);
			return -1;
		}
		else {
			execute_file(argv[3]);
		}
	}
	else {
		printf("Usage: %s\n", usage);
		return -1;
	}

	return 0;
}
